<!--
 * @LastEditTime: 2023-11-23 17:06:09
 * @FilePath: \bdcar-net\src\views\videoMonitoring\historyVideo\index.vue
 * @Description: 历史视频 
-->
<template>
  <div class="app-container">
    <div class="left-dom" id="rightDom" v-loading="treelLoading">
      <div class="title">
        <div class="rectangle"></div>监管对象列表
        <el-icon :size="20" @click="getTreeData" style="position: absolute;right: 27px;cursor: pointer;" title="刷新树">
          <Refresh />
        </el-icon>
      </div>
      <historyVideoTree class="leftTree" :treeList="treeList" ref="hvTree" :amount="true"
        style="height: calc(100% - 3rem)" />
      <div class="move-img" id="moveing" v-move></div>
    </div>
    <div class="right-dom" id="container">
      <div class="flv-video">
        <video v-if="selectedVideoType != 1" id="videoElement" controls autoplay muted class="myVideo"></video>
        <video v-else-if="selectedVideoType == 1 && videoUrl" controls autoplay muted class="myVideo">
          <source :src="videoUrl" type="video/mp4">
          您的浏览器不支持 HTML5 视频播放。
        </video>
        <div class="close-btn">
          <img @click="closeOneVideo()" src="../../../assets/img/video/closenew.png">
        </div>
      </div>
      <el-form :model="{}" ref="queryRef" :inline="true" label-width="90px" class="search-dom">
        <el-form-item label="查询时间" prop="queryTime">
          <el-date-picker v-model="queryTime" type="datetimerange" :clearable="false" range-separator="-"
            start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss"
            :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59),]"></el-date-picker>
        </el-form-item>
        <el-form-item label="视频类型" prop="selectedVideoType">
          <el-select v-model="selectedVideoType" placeholder="请选择" @change="queryParams.pageNum = 1; getTableData(true)">
            <el-option label="服务器视频" :value="1"></el-option>
            <el-option label="设备视频" :value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="流媒体类型" prop="mediaType">
          <el-select v-model="mediaType" placeholder="请选择">
            <el-option label="音视频" :value="0"></el-option>
            <el-option label="视频" :value="1"></el-option>
            <el-option label="音频" :value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="Search" @click="queryParams.pageNum = 1; getTableData(true)">搜索</el-button>
        </el-form-item>
      </el-form>
      <div class="base-table">
        <el-table highlight-current-row :data="tableData" v-loading="tableLoading" height="100%">
          <el-table-column :prop="v.prop" :label="v.name" v-for="v in tableItems" :key="v.prop" :width="v.width"
            show-overflow-tooltip>
            <template #default="scope">
              <dict-tag v-if="v.type == 'dict'" :options="sel_option[v.prop]" :value="scope.row[v.prop]">
                <template v-slot:pre>
                  <img v-if="v.type == 'dict'" src="../../../assets/img/video/videoPng.png"
                    style="position:relative;top:2px;margin-right:5px" />
                </template>
              </dict-tag>
              <span v-else-if="v.prop == 'fileSize'">{{
                getFileSize(scope.row['fileSize'] || scope.row['size']) }}</span>
              <span v-else> {{ scope.row[v.prop] }}</span>
            </template>
          </el-table-column>
          <el-table-column width="160" prop label="操作">
            <template #default="scope">
              <div @click="closeOneVideo()" class="close-btn"
                v-if="(scope.row.startTime == selectVideoTime.startTime && scope.row.endTime == selectVideoTime.endTime)">
                关闭
              </div>
              <div @click="setVideoUrl(scope.row)" class="play-btn" v-else>
                播放
              </div>
              <div class="close-btn" @click="downloadFlv(scope.row.httpUrl, scope.row)"
                v-if="downLoadRealtime[getKey(scope.row)] && downLoadRealtime[getKey(scope.row)].finished != 1">
                {{ downLoadRealtime[getKey(scope.row)].percent }}
              </div>
              <div @click="downloadFlv(scope.row.httpUrl, scope.row)" class="download-btn" v-else>
                下载
              </div>
              <div class="delete-btn" @click="deleteVideo(scope.row)" v-if='selectedVideoType == 1' v-hasPermi="['videoMonitoring:historyVideo:delete']">
                删除
              </div>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <div class="pagination">
        <pagination v-show="queryParams.total > 0" :total="queryParams.total" v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize" @pagination="getTableData" />
      </div>
    </div>
    <el-dialog title="选择时间" v-model="selectTimeOpen" width="400px" append-to-body>
      <div class="body" style="height: 100px">
        <el-form ref="queryRef" :model="{}" label-width="100px">
          <el-form-item label="开始时间" prop="startTime">
            <el-date-picker v-model="selectDownTime[0]" type="datetime" :disabled-date="disabledDate"
              placeholder="开始时间" />
          </el-form-item>
          <el-form-item label="结束时间" prop="endTime">
            <el-date-picker v-model="selectDownTime[1]" type="datetime" :disabled-date="disabledDate"
              placeholder="结束时间" />
          </el-form-item>
        </el-form>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSaveTime">确 定</el-button>
          <el-button @click="handleClose">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup name="HistoryVideo">
import { getVideoTree } from "@/api/videoMonitoring/realtimeVideo"
import { getHisVideoTable, getDvoHisVideo } from "@/api/videoMonitoring/historyVideo";
import historyVideoTree from './historyVideoTree';
import flvjs from 'flv.js';
import dayjs from "dayjs";
import request from '@/utils/request'
import axios from 'axios';
//存储播放的flvjs对象
let oneflvVideo = null;
let selectId = null;
// 大数据获取设备视频列表不能分页，故传递key以判断是读取设备列表还是读取缓存列表
let key = '';

const treelLoading = ref(false);
const tableLoading = ref(false);
const hvTree = ref(null);
// 所播放的视频开始时间结束时间-对于一个视频来说是唯一的
const selectVideoTime = ref({
  startTime: null,
  endTime: null,
});
const { proxy } = getCurrentInstance();
//当前选择的视频类型 1=服务器视频 2=设备视频
const selectedVideoType = ref(1);

const videoUrl = ref(null);
// 流媒体类型（0音视频,1音频,2视频,3视频或音频）
const mediaType = ref(0);

const queryParams = ref({
  total: 0,
  pageNum: 1,
  pageSize: 15
})
//表格参数
const tableData = ref([]);
//获取后台的初始数据
const tableItems = ref([
  { name: '光伏板编号', prop: "numberPlate" },
  { name: '设备号', prop: "dvo" },
  { name: '所属公司', prop: "companyName" },
  { name: '通道号', prop: "channel" },
  { name: '文件大小(Mb)', prop: "fileSize" },
  { name: '开始时间', prop: "startTime", width: '200' },
  { name: '结束时间', prop: "endTime", width: '200' },
  { name: '流媒体类型', prop: "streamType", type: "dict" },
]);
const queryTime = ref([]);
const treeList = ref([]);
const query = ref([]);
queryTime.value = [dayjs(new Date()).format("YYYY-MM-DD 00:00:00"), dayjs(new Date()).format("YYYY-MM-DD 23:59:59")];

// 下载-选择时间
const selectTimeOpen = ref(false)
const selectDownTime = ref([])
let currentSelectInfo = ref({})

function disabledDate(time) {
  let day1 = dayjs(dayjs(currentSelectInfo.startTime).format('YYYY-MM-DD HH:mm:ss')).unix();
  let day2 = dayjs(dayjs(currentSelectInfo.endTime).format('YYYY-MM-DD HH:mm:ss')).unix();
  return time.getTime() / 1000 > day2 || time.getTime() / 1000 < day1
}

getTreeData();
function getTreeData() {
  treelLoading.value = true
  getVideoTree().then(result => {
    treelLoading.value = false
    if (result.code == 200) {
      treeList.value = result.data;
    }
  });
}

const sel_option = ref({});
// 服务器视频下载-存储下载实时进度
const downLoadRealtime = ref({});

let cancelTokenSource = null

const dictKeys = [];
tableItems.value.forEach(v => {
  if (v.type == 'dict') {
    dictKeys.push(v.prop)
  }
});
sel_option.value = proxy.useDict.apply(this, [...dictKeys]);
import $store from "@/store/index";

// 得到downLoadRealtime对象的键值
function getKey(row) {
  return changeTime(row.startTime) + '_' + changeTime(row.endTime)
}
function getFileSize(val) {
  return Number(val / 1024 / 1024).toFixed(2)
}
//获取表格数据
function getTableData(setkey = false) {
  if (!query.value.channel) {
    proxy.$modal.msgError("请选择通道");
    return
  }
  tableLoading.value = true;
  let _data = {
    pageSize: queryParams.value.pageSize,
    pageIndex: queryParams.value.pageNum,
    startTime: queryTime.value[0],
    endTime: queryTime.value[1],
    deviceId: query.value.deviceId,
    mediaType: mediaType.value,
    channel: query.value.channel
  };
  if (setkey) {
    // 唯一值
    key = '' + $store.state.value.user.info.userId + new Date().getTime() + Math.floor(Math.random() * 100);
  }
  closeOneVideo(); //关闭播放
  closeDownloadFlv(); //关闭下载
  downLoadRealtime.value = {}  //清空设备视频正在下载的对象
  cancelTokenSource = null  //清空
  selectVideoTime.value = {
    startTime: null,
    endTime: null,
  } //清空
  videoUrl.value = null //清空
  tableData.value = []
  if (selectedVideoType.value == 1) {
    //服务器视频
    getServerHisVideoTable(_data);
  } else {
    //设备
    if (query.value.status == false) {
      proxy.$modal.msgError("离线设备没有设备视频！");
      tableLoading.value = false;
      return
    }
    getDvoHisVideoTable({ ..._data, tagId: key });
  }
}
//获取服务器视频
function getServerHisVideoTable(_data) {
  getHisVideoTable(_data).then((response) => {
    tableLoading.value = false
    if (response.code == 200) {
      tableData.value = response.data.list;
      queryParams.value.total = response.data.total;
    }
  }).catch((err) => {
    proxy.$modal.msgError("数据库信息查询失败！");
    tableLoading.value = false
  });
}
//获取设备视频
function getDvoHisVideoTable(_data) {
  getDvoHisVideo(_data).then((response) => {
    tableLoading.value = false
    if (response.code == 200) {
      tableData.value = response.data.list;
      queryParams.value.total = response.data.total;
    }
  }).catch((err) => {
    proxy.$modal.msgError("查询失败！");
    tableLoading.value = false
  });
}
function oneNodeClick(data, e) {
  if (data.type != 4) {
    proxy.$modal.msgError("只能选择通道");
    hvTree.value.$refs.myTree.setCurrentKey(selectId || null)
    return
  }
  selectId = data.id
  query.value.deviceId = data.deviceId
  query.value.channel = data.id.charAt(data.id.length - 1)
  query.value.status = e.parent.data.onlineStatus
  getTableData(true)
}
//单视频播放 
function playOneVideo(url) {
  // closeOneVideo();
  if (flvjs.isSupported()) {
    let videoElement = document.getElementById('videoElement');
    oneflvVideo = flvjs.createPlayer({
      type: 'flv',
      isLive: false,
      // hasAudio: obj.streamType == 0,
      url: url,
    });
    oneflvVideo.attachMediaElement(videoElement);
    oneflvVideo.load();
    oneflvVideo.play();
  }
  // 倍速播放
  // $nextTick(() => {
  //   videoElement.playbackRate = 5;
  // })
}
//单视频-关闭方法
function closeOneVideo() {
  if (oneflvVideo) {
    oneflvVideo.pause()
    oneflvVideo.unload()
    oneflvVideo.detachMediaElement()
    oneflvVideo.destroy()
    oneflvVideo = null
  }
  selectVideoTime.value = {
    startTime: null,
    endTime: null,
  };
  videoUrl.value = null;
}
// 中断设备视频下载
function closeDownloadFlv() {
  // 在需要中断请求的地方调用 cancel 方法
  if (cancelTokenSource) {
    cancelTokenSource.cancel('请求被取消');
    cancelTokenSource = null
    downLoadRealtime.value = {};
  }
}
// 设备
function handleSaveTime() {
  let obj = JSON.parse(JSON.stringify(currentSelectInfo))
  let time1 = dayjs(selectDownTime.value[0]).unix()
  let time2 = dayjs(selectDownTime.value[1]).unix()
  let rangetime1 = dayjs(obj.startTime).unix()
  let rangetime2 = dayjs(obj.endTime).unix()
  if (time1 < rangetime1 || time1 > rangetime2 || time2 < rangetime1 || time2 > rangetime2) {
    proxy.$modal.msgError('开始时间或结束时间超出范围');
    return
  }
  if (time1 > time2) {
    proxy.$modal.msgError('开始时间不能大于结束时间');
    return
  }
  let query = {
    startTime: dayjs(selectDownTime.value[0]).format("YYYY-MM-DD HH:mm:ss"),
    endTime: dayjs(selectDownTime.value[1]).format("YYYY-MM-DD HH:mm:ss"),
    dvo: obj.dvo,
    channel: obj.channel,
    mediaType: obj.streamType
  }
  request({
    url: '/comlbs/historyVideo/queryTargetHisMedia',
    method: 'post',
    data: query
  }).then(res => {
    if (res.code != 200) return
    // 存在服务器视频，则直接下载服务器视频
    if (res.data) {
      downloadFlvOne(res.data.httpUrl);
    } else {
      // 设备视频下载和播放不能同时,下载的时候关闭播放
      closeOneVideo();
      setWebsocketDownload();
    }
    handleClose()
  })
}
function handleClose() {
  selectTimeOpen.value = false
}
//下载视频
function downloadFlv(url, obj) {
  currentSelectInfo = obj
  if (selectedVideoType.value == 1) {
    //服务器视频
    downloadFlvOne(url);
  } else {
    // 继续点击当前正在下载的设备视频,中断下载
    let _key = changeTime(obj.startTime) + '_' + changeTime(obj.endTime)
    if (downLoadRealtime.value.hasOwnProperty(_key)) {
      closeDownloadFlv();
      return
    }
    selectTimeOpen.value = true
    nextTick(() => {
      selectDownTime.value = [obj.startTime, obj.endTime];
      // selectDownTime.value[1] = obj.endTime;
    })
  }
}
function setWebsocketDownload() {
  let obj = JSON.parse(JSON.stringify(currentSelectInfo))
  proxy.$modal.msgSuccess("获取资源中...");
  let starttime = changeTime(selectDownTime.value[0])
  let endtime = changeTime(selectDownTime.value[1])
  let key = changeTime(obj.startTime) + '_' + changeTime(obj.endTime) //唯一键值
  closeDownloadFlv(); //下载前要先中断上一个下载

  let diffTime1 = dayjs(obj.startTime).unix() - dayjs(obj.endTime).unix()
  let diffTime2 = dayjs(selectDownTime.value[0]).unix() - dayjs(selectDownTime.value[1]).unix()
  let _fileSize = parseInt(obj.fileSize * (diffTime2 / diffTime1) / 1024)

  // finished 0：未完成 1：下载成功 2：下载失败
  downLoadRealtime.value[key] = {
    percent: '等待下载开始',
    finished: 0
  };
  //http的chunk请求 
  const httpUrl = GlobalSetting.IotConfig.videoHttp + obj.dvo + '_' + obj.channel + '_' + starttime + '_' + endtime + '_' + _fileSize + '_download.media';
  // 创建一个 CancelToken.source 实例
  cancelTokenSource = axios.CancelToken.source();
  axios.get(httpUrl, {
    responseType: 'text',
    cancelToken: cancelTokenSource.token,
    onDownloadProgress: function (event) {
      // 已接收的数据长度event.loaded
      // 获取当前chunk的数据
      let chunkArr = event.target.response.split('\r\n') //截取数据块
      let chunk = JSON.parse(chunkArr[chunkArr.length - 2]);
      console.log(chunk);
      downLoadRealtime.value[key] = {
        percent: chunk.percent,
        finished: chunk.finished
      };
      if (chunk.finished == 1) {
        downloadFlvOne(chunk.loadUrl);
        delete downLoadRealtime.value[key]
      }
      else if (chunk.finished == 2) {
        proxy.$modal.msgError("下载失败");
        delete downLoadRealtime.value[key]
      }
    }
  })
}
//下载服务器视频  type 1服务器视频下载 2设备视频下载
function downloadFlvOne(url) {
  let _starttime, _endtime
  if (selectedVideoType.value == 2) {
    _starttime = dayjs(selectDownTime.value[0]).format("YYYYMMDDHHmmss")
    _endtime = dayjs(selectDownTime.value[1]).format("YYYYMMDDHHmmss")
  } else {
    _starttime = dayjs(currentSelectInfo.startTime).format("YYYYMMDDHHmmss")
    _endtime = dayjs(currentSelectInfo.endTime).format("YYYYMMDDHHmmss")
  }
  let name = currentSelectInfo.numberPlate + "-" + currentSelectInfo.channel + "-" + _starttime + '-' + _endtime + ".mp4";
  // 保存到本地并自动点击
  function saveAs(data, name) {
    const urlObject = window.URL || window.webkitURL || window;
    const export_blob = new Blob([data]);
    const save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
    save_link.href = urlObject.createObjectURL(export_blob);
    save_link.download = name;
    save_link.click();
  }
  // 下载含有url的文件
  function downloadUrlFile(url, fileName) {
    proxy.$modal.msgSuccess("浏览器正在下载视频,请稍后");
    const url2 = url.replace(/\\/g, '/');
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url2, true);
    xhr.responseType = 'blob';
    //xhr.setRequestHeader('Authorization', 'Basic a2VybWl0Omtlcm1pdA==');
    // 为了避免大文件影响用户体验，建议加loading
    xhr.onload = () => {
      if (xhr.status === 200) {
        // 获取文件blob数据并保存
        saveAs(xhr.response, fileName);
      }
    };
    xhr.send();
  }
  downloadUrlFile(url, name);
}
// 修改时间-时间转化为YYYYMMDDHHmmss
function changeTime(_time) {
  return dayjs(_time).format("YYYYMMDDHHmmss")
}
//设置视频
async function setVideoUrl(obj) {
  await closeOneVideo();
  closeDownloadFlv();
  selectVideoTime.value = {
    startTime: obj.startTime,
    endTime: obj.endTime,
  }
  // 根据这个selectedVideoType区分播放的是服务器视频还是车载视频
  if (selectedVideoType.value == 1) {
    //服务器视频
    videoUrl.value = obj.httpUrl;
    return
  } else {
    videoUrl.value = GlobalSetting.IotConfig.videoHttp + obj.dvo
      + '_' + obj.channel
      + "_" + changeTime(obj.startTime)
      + "_" + changeTime(obj.endTime)
      + "_" + parseInt(obj.fileSize / 1024)
      + "_" + 'play.media'
  }
  playOneVideo(videoUrl.value);
}
function deleteVideo(row) {
  proxy.$modal.confirm('是否确认删除该视频？').then(() => {
    request({
      url: '/comlbs/historyVideo/deleteHisMediaList',
      method: 'post',
      data: [row.rowKey]
    }).then(res => {
      if (res.code != 200) return
      proxy.$modal.msgSuccess("删除成功");
    })
  })
}
const vMove = {
  beforeMount: (el) => {
    el.onmousedown = function (e) {
      var init = e.clientX;
      //要移动的右侧窗体
      var parent = document.getElementById("rightDom");
      var initWidth = parent.offsetWidth;
      document.onselectstart = () => {
        return false
      };
      document.ondragstart = () => {
        return false
      };
      let minWidth = 270;
      let maxWidth = 600;
      //被动移动的左侧窗体
      var otherDom = document.getElementById("container");
      var OtherinitWidth = otherDom.offsetWidth;
      var moveDom = document.getElementById("moveing");
      var moveinitLeft = moveDom.offsetLeft;
      document.onmousemove = function (e) {
        var end = e.clientX;

        if (initWidth + end - init > minWidth && initWidth + end - init < maxWidth) {
          parent.style.width = initWidth + end - init + "px"
          moveDom.style.left = moveinitLeft + end - init + "px"
          otherDom.style.width = OtherinitWidth - end + init + "px";
        }
        else return
      };
      document.onmouseup = function () {
        document.onselectstart = null;
        document.ondragstart = null;
        document.onmousemove = document.onmouseup = null;
      };
    };
  }
}
defineExpose({
  oneNodeClick
});
</script>
<style scoped lang="scss">
.left-dom {
  width: 280px;
  height: 100%;
  background: #fff;
  margin-right: 16px;
  position: absolute;
  left: 0;

  .move-img {
    background: url("../../../assets/img/moving.png") no-repeat center;
    width: 10px;
    height: 100%;
    cursor: col-resize;
    position: absolute;
    left: 270px;
    top: 0;
  }

  .leftTree {
    width: calc(100% - 40px);
    height: calc(100% - 70px) !important;
    margin-left: 20px;
    margin-top: 5px;
  }
}

.title {
  height: 31px;
  font-weight: 600;
  color: #424242;
  background-color: #e6efff;
  display: flex;
  align-items: center;
  margin: 16px 20px;
  margin-bottom: 0;
  font-size: 16px;
  color: #424242;
  display: flex;
  align-items: center;
}


.right-dom {
  width: calc(100% - 296px);
  height: 100%;
  display: inline-flex;
  flex-direction: column;
  position: absolute;
  right: 0;
  background: #fff;


  .search-dom {
    padding: 18px 0 0 25px;
  }

  .flv-video {
    width: 100%;
    height: 500px;
    border-radius: 6px;
    background: #000;
    display: flex;
    justify-content: space-around;

    .myVideo {
      box-sizing: border-box;
      width: 100%;
      height: 100%;
      flex-shrink: 0;
    }

    .close-btn {
      position: absolute;
      cursor: pointer;
      right: 8px;
      top: 8px;
    }
  }

  .base-table {
    height: calc(100% - 634px);

    .base-btn {
      // width: 28px;
      line-height: 24px;
      border-radius: 2px;
      display: inline-block;
      text-align: center;
      color: #3c7bf8;
      margin-right: 10px;
      cursor: pointer;
      font-size: 14px;

      &:last-of-type {
        margin-right: 0;
      }
    }

    .close-btn {
      @extend .base-btn;
      color: #ea5757;
    }

    .download-btn {
      @extend .base-btn;

    }

    .play-btn {
      @extend .base-btn;

    }

    .delete-btn {
      @extend .base-btn;

    }
  }

  .pagination {
    padding: 0 8px;

    .el-pagination {
      float: right;
    }
  }
}
</style>
